package tmt.usercenter.web.service;

import com.tmt.annotation.ReflectInvoked;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tmt.usercenter.web.domain.ClientSystem;
import tmt.usercenter.web.domain.TmtAuthority;
import tmt.usercenter.web.domain.TmtUser;
import tmt.usercenter.web.domain.TmtUserRole;
import tmt.usercenter.web.repository.ITmtAuthorityRepo;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class TmtAuthorityService extends TmtBaseService<TmtAuthority, Long> {

    @Getter
    private ITmtAuthorityRepo iTmtAuthorityRepo;

    private ClientSystemService clientSystemService;

    private TmtRoleAuthorityService roleAuthorityService;

    private TmtUserService userService;

    private TmtUserRoleService userRoleService;

    @Autowired
    public TmtAuthorityService(ITmtAuthorityRepo iTmtAuthorityRepo,
                               ClientSystemService clientSystemService,
                               TmtRoleAuthorityService roleAuthorityService,
                               TmtUserRoleService userRoleService,
                               TmtUserService userService) {
        super(iTmtAuthorityRepo, Arrays.asList(clientSystemService, roleAuthorityService, userService));
        this.iTmtAuthorityRepo = iTmtAuthorityRepo;
        this.clientSystemService = clientSystemService;
        this.roleAuthorityService = roleAuthorityService;
        this.userRoleService = userRoleService;
        this.userService = userService;
    }


    @Transactional(readOnly = true)
    @Cacheable(value = "tmtAuthority", key = "'tmtAuthority_findById_' + #id", unless = "#result == null")
    public TmtAuthority findById(Long id) {
        return iTmtAuthorityRepo.findById(id);
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtAuthority", key = "'tmtAuthority_findByName_' + #name", unless = "#result == null")
    public TmtAuthority findByName(String name) {
        return iTmtAuthorityRepo.findByName(name);
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtAuthority", key = "'tmtAuthority_findByAuthority_' + #authority", unless = "#result == null")
    public TmtAuthority findByAuthority(String authority) {
        return iTmtAuthorityRepo.findByAuthority(authority);
    }

    /**
     * 通过角色获取角色的 distinct 权限
     *
     * 禁止删除该方法：反射调用的
     *
     * @param userId 角色编号列表
     * @return distinct 权限
     */
    @Transactional(readOnly = true)
    @ReflectInvoked("本方法被反射调用了")
    public List<TmtAuthority> findAuthoritiesByUserId(Long userId) {
        List<TmtUserRole> userRoles = userRoleService.findAllByUserId(userId);

        Vector<TmtAuthority> result = new Vector<>();
        userRoles.forEach(x -> x.getRole().getRoleAuthorities().forEach(y -> {
            if (result.stream().noneMatch(z -> Objects.equals(z.getId(), y.getAuthority().getId()))) {
                result.add(y.getAuthority());
            }
        }));
        return result;
    }

    @Transactional(readOnly = true)
    public List<TmtAuthority> findByClientId(String clientId) {
        Specification<TmtAuthority> s = (root, query, cb) -> {
            Predicate p = cb.equal(root.get("originSystem").get("clientId"), clientId);
            return query.where(p).getRestriction();
        };
        return iTmtAuthorityRepo.findAll(s);
    }

    public boolean updateAuthorities(List<TmtAuthority> tmtAuthorities) {
        if (tmtAuthorities == null || tmtAuthorities.size() <= 0)
            return true;

        //获取客户系统对象。
        String clientId = tmtAuthorities.get(0).getOriginSystem().getClientId();
        ClientSystem cs = clientSystemService.findByClientId(clientId);

        //获取客户系统的创建者。
        TmtUser creator = cs.getCreator();

        //获取客户系统当前所有的权限对象。
        List<TmtAuthority> allList = findByClientId(clientId);

        List<TmtAuthority> newList = new ArrayList<>();

        tmtAuthorities.forEach(x -> {
            Optional<TmtAuthority> optional = allList.stream()
                    .filter(y -> Objects.equals(y.getAuthority(), x.getAuthority()))
                    .findFirst();

            TmtAuthority authority = new TmtAuthority();
            if (!optional.isPresent()) {
                authority.setAuthority(x.getAuthority());
                authority.setOriginSystem(cs);
                authority.setCreator(creator);
            } else {
                authority = optional.get();
            }

            authority.setName(x.getName());
            authority.setDescription(x.getDescription());
            authority.setClientTag(x.getClientTag());
            authority.setAccessPoint(x.getAccessPoint());
            authority.setSort(x.getSort());
            authority.setVisible(x.isVisible());
            authority.setOperationType(x.getOperationType());
            authority.setAuthorityType(x.getAuthorityType());

            newList.add(authority);
        });

        List<TmtAuthority> savedList = iTmtAuthorityRepo.save(newList);

        //构造权限间的关系。
        tmtAuthorities.forEach(x -> {
            if (StringUtils.isEmpty(x.getParentCode()))
                return;

            Optional<TmtAuthority> optional = savedList.stream()
                    .filter(y -> Objects.equals(y.getAuthority(), x.getAuthority()))
                    .findFirst();
            if (!optional.isPresent())
                return;

            TmtAuthority authority = optional.get();

            Optional<TmtAuthority> parent = savedList.stream()
                    .filter(y -> Objects.equals(y.getAuthority(), x.getParentCode()))
                    .findFirst();

            if (!parent.isPresent())
                return;

            authority.setParent(parent.get());

            iTmtAuthorityRepo.save(authority);
        });

        //删除已经废弃的权限对象。
        List<TmtAuthority> deletedList = allList.stream()
                .filter(x -> tmtAuthorities.stream().noneMatch(y -> Objects.equals(y.getAuthority(), x.getAuthority())))
                .collect(Collectors.toList());

        //清除子权限中的无效项。
        deletedList.forEach(x -> {
            if (x.getChildren() != null && x.getChildren().size() > 0) {
                List<TmtAuthority> invalidList = x.getChildren().stream()
                        .filter(y -> !Objects.equals(y.getParent().getId(), x.getId()))
                        .collect(Collectors.toList());
                x.getChildren().removeAll(invalidList);
            }
        });

        while (deletedList.size() > 0) {
            //优先删除Children列表为空的(即最底层的)权限
            List<TmtAuthority> deletedItems = new ArrayList<>();
            deletedList.stream().filter(x -> x.getChildren() == null || x.getChildren().size() <= 0)
                    .forEach(x -> {
                        try {
                            deleteById(x.getId());
                            if (x.getParent() != null && x.getParent().getChildren() != null) {
                                x.getParent().getChildren().remove(x);
                            }
                            deletedItems.add(x);
                        } catch (Exception ex) {
                        }
                    });
            deletedList.removeAll(deletedItems);
        }

        return true;
    }


    /***************************私有方法***************************/


    private void deleteById(Long authorityId) {
        //先删除角色和权限的映射关系。
        roleAuthorityService.deleteByAuthorityId(authorityId);
        iTmtAuthorityRepo.delete(authorityId);
    }

}
